/*****************************************************
** Author:
** Date: 2019-11-04
*
*****************************************************/

#include "script.h"
#include "dnalua.h"
#include "core.h"

/**
 * @brief
 * @param  None
 * @retval None
 */
int lua_arch_gpio_init(lua_State *l)
{
    if (lua_getglobal(l, LUA_ARCH_MODULE_GPIO_INIT_METHOD) != LUA_TFUNCTION) {
        lua_pop(l, 1);
        lua_log_inf("lua 'gpio' missing");
        return LUA_ARCH_ERR_SCRIPT;
    }

    int err;
    if ((err = lua_pcall(l, 0, 0, 0)) != LUA_OK) {
        lua_log_err("lua_pcall failed %d", err);
        return -LUA_ARCH_ERR_SCRIPT;
    }

    print_lua_stack(l, "gpio init");
    lua_log_inf("gpio script read done");
    return LUA_ARCH_OK;
}

int lua_arch_gpio_deinit(lua_State *l)
{
    lua_log_inf("gpio deinit");
    return LUA_ARCH_OK;
}

int lua_arch_uart_init(lua_State* l, struct lua_arch_uart *uart)
{
    if (lua_getglobal(l, LUA_ARCH_MODULE_UART_INIT_METHOD) != LUA_TFUNCTION) {
        lua_pop(l, 1);
        lua_log_inf("lua 'uart' missing");
        return LUA_ARCH_OK;
    }

    int err, type , ret = LUA_ARCH_OK;
    if ((err = lua_pcall(l, 0, 1, 0)) != LUA_OK) {
        lua_log_err("lua_pcall failed %d", err);
        return -LUA_ARCH_ERR_SCRIPT;
    }

    // get 'id'
    lua_pushstring(l, LUA_ARCH_MODULE_UART_ID);
    if ((type = lua_gettable(l, -2)) != LUA_TNUMBER) {
        lua_log_err("get '%s' type failed %d", LUA_ARCH_MODULE_UART_ID, type);
        ret = -LUA_ARCH_ERR_SCRIPT;
        goto err;
    }
    uart->id = lua_tointeger(l, -1);
    lua_log_inf("uart id %d", uart->id);
    lua_pop(l, 1);

    // get 'baud'
    lua_pushstring(l, LUA_ARCH_MODULE_UART_BAUD);
    if ((type = lua_gettable(l, -2)) != LUA_TNUMBER) {
        lua_log_err("get '%s' type failed %d", LUA_ARCH_MODULE_UART_BAUD, type);
        ret = -LUA_ARCH_ERR_SCRIPT;
        goto err;
    }
    uart->baud = lua_tointeger(l, -1);
    lua_log_inf("uart baud %d", uart->baud);
    lua_pop(l, 1);

    // get 'bits'
    lua_pushstring(l, LUA_ARCH_MODULE_UART_BITS);
    if ((type = lua_gettable(l, -2)) != LUA_TNUMBER) {
        lua_log_err("get '%s' type failed %d", LUA_ARCH_MODULE_UART_BITS, type);
        ret = -LUA_ARCH_ERR_SCRIPT;
        goto err;
    }
    int bits = lua_tointeger(l, -1);
    lua_log_inf("uart bits %d", bits);
#ifdef OS_LINUX
    uart->bits = bits;
#elif defined(OS_DNA)
    switch (bits) {
        case 8: uart->bits = DNA_UART_8BITS; break;
        case 9: uart->bits = DNA_UART_9BITS; break;
        default: lua_log_err("uart bits err %d", bits); break;
    }
#endif
    lua_pop(l, 1);

    // get 'parity'
    lua_pushstring(l, LUA_ARCH_MODULE_UART_PARITY);
    if ((type = lua_gettable(l, -2)) != LUA_TSTRING) {
        lua_log_err("get '%s' type failed %d", LUA_ARCH_MODULE_UART_PARITY, type);
        ret = -LUA_ARCH_ERR_SCRIPT;
        goto err;
    }
    char *parity = (char *)lua_tostring(l, -1);
    lua_log_inf("uart parity %s", parity);
    if (!strcmp(parity, "none")) {
#ifdef OS_LINUX
        uart->parity = 'n';
#elif defined(OS_DNA)
        uart->parity = DNA_UART_PARITY_NONE;
#endif
    }
    else if (!strcmp(parity, "odd")) {
#ifdef OS_LINUX
        uart->parity = 'o';
#elif defined(OS_DNA)
        uart->parity = DNA_UART_PARITY_ODD;
#endif
    }
    else if (!strcmp(parity, "even")) {
#ifdef OS_LINUX
        uart->parity = 'e';
#elif defined(OS_DNA)
        uart->parity = DNA_UART_PARITY_EVEN;
#endif
    }
    else {
        lua_log_err("uart parity err %s", parity);
    }
    lua_pop(l, 1);

    // get 'stop'
    lua_pushstring(l, LUA_ARCH_MODULE_UART_STOP);
    if ((type = lua_gettable(l, -2)) != LUA_TNUMBER) {
        lua_log_err("get '%s' type failed %d", LUA_ARCH_MODULE_UART_STOP, type);
        ret = -LUA_ARCH_ERR_SCRIPT;
        goto err;
    }
    int stop = lua_tointeger(l, -1);
    lua_log_inf("uart stop %d", stop);
#ifdef OS_LINUX
    uart->stop = stop;
#elif defined(OS_DNA)
    switch (stop) {
        case 1: uart->stop = DNA_UART_STOP_1BITS; break;
        case 2: uart->stop = DNA_UART_STOP_2BITS; break;
        default: lua_log_err("uart stop err %d", stop); break;
    }
#endif
    lua_pop(l, 1);

    // get 'fit'
    lua_pushstring(l, LUA_ARCH_MODULE_UART_FIT);
    if ((type = lua_gettable(l, -2)) != LUA_TNUMBER) {
        lua_log_err("get '%s' type failed %d", LUA_ARCH_MODULE_UART_FIT, type);
        ret = -LUA_ARCH_ERR_SCRIPT;
        goto err;
    }
    uart->fit = lua_tointeger(l, -1);
    lua_log_inf("uart fit %d", uart->fit);
    lua_pop(l, 1);

err:
    lua_pop(l, 1); // pop table

    print_lua_stack(l, "uart init");
    lua_log_inf("uart script read done");
    return ret;
}

int lua_arch_uart_deinit(lua_State* l)
{
    lua_log_inf("uart deinit");
    return LUA_ARCH_OK;
}

int lua_arch_devinfo_init(lua_State* l, struct lua_arch_devinfo *devinfo)
{
    if (lua_getglobal(l, LUA_ARCH_MODULE_DEVINFO_INIT_METHOD) != LUA_TFUNCTION) {
        lua_pop(l, 1);
        return LUA_ARCH_OK;
    }

    int err, type, ret = LUA_ARCH_OK;
    if ((err = lua_pcall(l, 0, 1, 0)) != LUA_OK) {
        lua_log_err("lua_pcall failed %d", err);
        return -LUA_ARCH_ERR_SCRIPT;
    }

    // get 'devtype'
    lua_pushstring(l, LUA_ARCH_MODULE_DEVINFO_DEVTYPE);
    if ((type = lua_gettable(l, -2)) != LUA_TNUMBER) {
        lua_pop(l, 1);
        ret = -LUA_ARCH_ERR_SCRIPT;
        lua_log_err("get '%s' type failed %d", LUA_ARCH_MODULE_DEVINFO_DEVTYPE, type);
        goto err;
    }
    devinfo->devtype = lua_tointeger(l, -1);
    lua_log_inf("devinfo devtype %d", devinfo->devtype);
    lua_pop(l, 1);

    // get 'name'
    lua_pushstring(l, LUA_ARCH_MODULE_DEVINFO_NAME);
    if ((type = lua_gettable(l, -2)) != LUA_TSTRING) {
        lua_pop(l, 1);
        ret = -LUA_ARCH_ERR_SCRIPT;
        lua_log_err("get '%s' type failed %d", LUA_ARCH_MODULE_DEVINFO_NAME, type);
        goto err;
    }
    strcpy(devinfo->name, (char *)lua_tostring(l, -1));
    lua_log_inf("devinfo name %s", devinfo->name);
    lua_pop(l, 1);

    // get 'hostname'
    lua_pushstring(l, LUA_ARCH_MODULE_DEVINFO_HOSTNAME);
    if ((type = lua_gettable(l, -2)) != LUA_TSTRING) {
        lua_pop(l, 1);
        ret = -LUA_ARCH_ERR_SCRIPT;
        lua_log_err("get '%s' type failed %d", LUA_ARCH_MODULE_DEVINFO_HOSTNAME, type);
        goto err;
    }
    char *hostname = (char *)lua_tostring(l, -1);
    lua_log_inf("hostname name %s", hostname);
    lua_pop(l, 1);

    // get 'pid'
    lua_pushstring(l, LUA_ARCH_MODULE_DEVINFO_PID);
    if ((type = lua_gettable(l, -2)) != LUA_TSTRING) {
        lua_pop(l, 1);
        ret = -LUA_ARCH_ERR_SCRIPT;
        lua_log_err("get '%s' type failed %d", LUA_ARCH_MODULE_DEVINFO_PID, type);
        goto err;
    }
    int hexsize = str2hex((char *)lua_tostring(l, -1), devinfo->pid, sizeof(devinfo->pid));
    dump_memory("pid", devinfo->pid, hexsize);
    lua_pop(l, 1);

err:
    lua_pop(l, 1); // pop table

    print_lua_stack(l, "devinfo init");
    lua_log_inf("devinfo script read done");
    return ret;
}

int lua_arch_clouds_init(lua_State* l, unsigned int *mask)
{
    if (lua_getglobal(l, LUA_ARCH_MODULE_CLOUDS_INIT_METHOD) != LUA_TFUNCTION) {
        lua_pop(l, 1);
        lua_log_inf("lua 'clouds' missing");
        return LUA_ARCH_ERR_SCRIPT;
    }

    int ret = LUA_ARCH_OK, err, type, i;
    char *luastr;
    print_lua_stack(l, "clouds init before pcall");
    if ((err = lua_pcall(l, 0, 1, 0)) != LUA_OK) {
        lua_log_err("lua_pcall failed %d", err);
        return -LUA_ARCH_ERR_SCRIPT;
    }
    print_lua_stack(l, "clouds init after pcall");

    // get 'clds'
    lua_pushstring(l, LUA_ARCH_MODULE_CLOUDS_CLDS);
    if ((type = lua_gettable(l, -2)) != LUA_TTABLE) {
        lua_pop(l, 1);
        ret = -LUA_ARCH_ERR_SCRIPT;
        lua_log_err("get '%s' type failed %d", LUA_ARCH_MODULE_CLOUDS_CLDS, type);
        goto err;
    }
    print_lua_stack(l, "tag1");

    *mask = 0x0;
    // skip table[0] which is invalid
    for (i = 1; (type = lua_rawgeti(l, -1, i)) != 0; ++i) {
        lua_log_dbg("[%d] type %s return %s", i, lua_typename(l, type), lua_tostring(l, -1));
        luastr = lua_tostring(l, -1);
        if (!strcmp(luastr, "bl"))
            *mask |= (1 << (PORT_ID_BL - DNALUA_BASE_CLOUDS_ID));
        else if (!strcmp(luastr, "hi"))
            *mask |= (1 << (PORT_ID_HI - DNALUA_BASE_CLOUDS_ID));

        print_lua_stack(l, "tag2");
        lua_pop(l, 1);
    }
    lua_log_inf("clouds mask 0x%x", *mask);
    print_lua_stack(l, "tag3");
    lua_pop(l, 2);

err:
    lua_pop(l, 1);

    print_lua_stack(l, "clouds init");
    lua_log_inf("clouds script read done");
    return ret;
}

int lua_arch_startup(lua_State* l, int mode)
{
    int err, ret = -LUA_ARCH_ERR_SCRIPT;

    lua_log_inf("enter mode %d", mode);
    lua_arch_lock();

    if (lua_getglobal(l, LUA_ARCH_MODULE_STARTUP_METHOD) != LUA_TFUNCTION) {
        lua_log_wrn("call %s failed", LUA_ARCH_MODULE_STARTUP_METHOD);
        lua_pop(l, 1);
    }
    else
    {
        lua_pushinteger(l, mode);

        print_lua_stack(l, "startup before pcall");
        if ((err = lua_pcall(l, 1, 0, 0)) != LUA_OK) {
            lua_log_err("lua_pcall failed %d %s", err, lua_tostring(l, -1));
            lua_pop(l, 1);
        }
        else {
            ret = LUA_ARCH_OK;
        }
    }

    print_lua_stack(l, "startup exit");
    lua_arch_unlock();
    return ret;
}

int lua_arch_network(lua_State* l, int status)
{
    int err, ret = -LUA_ARCH_ERR_SCRIPT;

    lua_log_inf("enter status %d", status);
    lua_arch_lock();

    if (lua_getglobal(l, LUA_ARCH_MODULE_NETWORK_METHOD) != LUA_TFUNCTION) {
        lua_log_wrn("call %s failed", LUA_ARCH_MODULE_NETWORK_METHOD);
        lua_pop(l, 1);
    }
    else {
        lua_pushinteger(l, status);

        print_lua_stack(l, "network before pcall");
        if ((err = lua_pcall(l, 1, 0, 0)) != LUA_OK) {
            lua_log_err("lua_pcall failed %d %s", err, lua_tostring(l, -1));
            lua_pop(l, 1);
        }
        else {
            ret = LUA_ARCH_OK;
        }
    }

    print_lua_stack(l, "network exit");
    lua_arch_unlock();
    return ret;
}

int lua_arch_bin2kv(lua_State* l, unsigned int cldid, \
                    unsigned char *binin, int bininlen, \
                    char *kvin, int kvinlen, \
                    char *snapshot, int snapmaxsize, \
                    char *kvout, int kvoutmaxsize)
{
    lua_log_dbg("enter cldid %d bin inlen %d kv inlen %d", cldid, bininlen, kvinlen);
    lua_arch_lock();

    if (lua_getglobal(l, LUA_ARCH_MODULE_BIN2KV_METHOD) != LUA_TFUNCTION) {
        lua_pop(l, 1);
        lua_arch_unlock();
        return -LUA_ARCH_ERR_SCRIPT;
    }

    lua_pushinteger(l, cldid);
    (bininlen == 0 || binin == NULL) ? lua_pushnil(l) : lua_arch_pushtable(l, binin, bininlen);
    (kvinlen == 0 || kvin == NULL) ?  lua_pushnil(l) : lua_pushstring(l, kvin);
    (snapshot == NULL) ? lua_pushnil(l) : lua_pushstring(l, snapshot);

    int err, type, kvsize = 0, snapsize = 0;
    char *luastr;

    print_lua_stack(l, "bin2kv before pcall");
    if ((err = lua_pcall(l, 4, 2, 0)) != LUA_OK) {
        lua_log_err("lua_pcall failed %d %s", err, lua_tostring(l, -1));
    }
    else {
        print_lua_stack(l, "bin2kv after pcall");

        // output snapshot
        if (snapshot != NULL) {
            if ((type = lua_type(l, -1)) != LUA_TSTRING) {
                lua_log_err("unkown type %s", lua_typename(l, type));
                snapshot[0] = '\0';
            }
            else {
                luastr = (char *)lua_tostring(l, -1);
                lua_log_dbg("get string(%d):%s", -1, luastr);
                if ((snapsize = strlen(luastr)) >= snapmaxsize) {
                    lua_log_wrn("snap size %d excced %d", snapsize, snapmaxsize);
                    snapsize = 0;
                    snapshot[0] = '\0';
                }
                else {
                    strcpy(snapshot, luastr);
                }
            }
        }

        // output kv string
        if (kvout != NULL) {
            if ((type = lua_type(l, -2)) != LUA_TSTRING) {
                lua_log_wrn("unkown type %s", lua_typename(l, type));
                kvout[0] = '\0';
            }
            else {
                luastr = (char *)lua_tostring(l, -2);
                lua_log_dbg("get string(%d):%s", -2, luastr);
                if ((kvsize = strlen(luastr)) >= kvoutmaxsize) {
                    lua_log_wrn("kv size %d excced %d", kvsize, kvoutmaxsize);
                    kvsize = 0;
                    kvout[0] = '\0';
                }
                else {
                    strcpy(kvout, luastr);
                }
            }
        }

        lua_pop(l, 2);
    }

    print_lua_stack(l, "bin2kv exit");
    lua_arch_unlock();
    return LUA_ARCH_OK;
}

int lua_arch_kv2bin(lua_State* l, int cloudid, \
                    char *kvin, int kvinlen, \
                    char *snapshot, int snapmaxsize, \
                    char *kvout, int kvoutmaxsize)
{
    lua_log_dbg("enter cloudid %d kv inlen %d", cloudid, kvinlen);
    lua_arch_lock();

    if (lua_getglobal(l, LUA_ARCH_MODULE_KV2BIN_METHOD) != LUA_TFUNCTION) {
        lua_pop(l, 1);
        lua_arch_unlock();
        return -LUA_ARCH_ERR_SCRIPT;
    }

    lua_pushinteger(l, cloudid);
    (kvinlen == 0 || kvin == NULL) ?  lua_pushnil(l) : lua_pushstring(l, kvin);
    (snapshot == NULL) ? lua_pushnil(l) : lua_pushstring(l, snapshot);

    int err, type, kvsize = 0, snapsize = 0;
    char *luastr;

    print_lua_stack(l, "kv2bin defore pcall");
    if ((err = lua_pcall(l, 3, 2, 0)) != LUA_OK) {
        lua_log_err("lua_pcall failed %d %s", err, lua_tostring(l, -1));
    }
    else {
        print_lua_stack(l, "kv2bin after pcall");

        // output snapshot
        if (snapshot != NULL) {
            if ((type = lua_type(l, -1)) != LUA_TSTRING) {
                lua_log_err("unkown type %s", lua_typename(l, type));
                snapshot[0] = '\0';
            }
            else {
                luastr = (char *)lua_tostring(l, -1);
                lua_log_dbg("get string(%d):%s", -1, luastr);
                if ((snapsize = strlen(luastr)) >= snapmaxsize) {
                    lua_log_wrn("snap size %d excced %d", snapsize, snapmaxsize);
                    snapsize = 0;
                    snapshot[0] = '\0';
                }
                else {
                    strcpy(snapshot, luastr);
                }
            }
        }

        // output kv string
        if (kvout != NULL) {
            if ((type = lua_type(l, -2)) != LUA_TSTRING) {
                lua_log_wrn("unkown type %s", lua_typename(l, type));
                kvout[0] = '\0';
            }
            else {
                luastr = (char *)lua_tostring(l, -2);
                lua_log_dbg("get string(%d):%s", -2, luastr);
                if ((kvsize = strlen(luastr)) >= kvoutmaxsize) {
                    lua_log_wrn("kv size %d excced %d", kvsize, kvoutmaxsize);
                    kvsize = 0;
                    kvout[0] = '\0';
                }
                else {
                    strcpy(kvout, luastr);
                }
            }
        }

        lua_pop(l, 2);
    }

    print_lua_stack(l, "kv2bin end");
    lua_arch_unlock();
    return LUA_ARCH_OK;
}

/* End of file */



